package stardust.core.network.protocol.http;


import java.io.IOException;
import java.net.URL;
import stardust.core.io.IOUtils;
import stardust.core.log.Log;
import stardust.core.network.NetworkSession;
import stardust.core.network.NetworkSessionInputStream;
import stardust.core.network.NetworkSessionOutputStream;
import stardust.core.network.NetworkState;
import stardust.core.network.ProtocolType;
import stardust.core.support.PatternUtils;


public class HttpClient
{
  
  public static final Log log = new Log(HttpClient.class);

  private String userAgent;

  private NetworkSession clientSocket;

  private Cookie cookie = new Cookie();

  private URL referer;
  
  private HttpRequestMeta lastRequest;

  public HttpClient()
  {
    this(HttpClient.class.getName());
  }

  public HttpClient(String userAgent)
  {
    this.userAgent = userAgent;
  }

  private void initializeNetwork(String host, int port)
  {
    if (this.clientSocket == null)
    {
      this.clientSocket = NetworkSession.getInstance(ProtocolType.tcp);
    }
    else if (!this.clientSocket.getRemoteHost().equalsIgnoreCase(host) || this.clientSocket.getRemotePort() != port)
    {
      this.clientSocket.disconnect();
    }

    if (this.clientSocket.getState() == NetworkState.closed)
    {
      this.clientSocket.getConfiguration().setTimeout(1000 * 30);
      this.clientSocket.apply();
      
      this.clientSocket.connect(host, port);
    }
    
    this.clientSocket.getConfiguration().setTimeout(1000 * 30);
    this.clientSocket.apply();
  }
  
  public void setReferer(URL referer)
  {
	  this.referer = referer;
  }

  /**
   * 检测当前是否与服务器建立连接。
   */
  public boolean isConnected()
  {
    if (this.clientSocket == null)
    {
      return false;
    }

    return this.clientSocket.getState() == NetworkState.open;
  }

  /**
   * 强制断开与服务器的连接。
   */
  public void disconnect()
  {
    if (this.clientSocket != null)
    {
      this.clientSocket.disconnect();
    }
  }

  /**
   * 访问任意URL地址。
   * <p/>
   * @param addr
  * 跳转到绝对URL地址。
   * @param method
* 指定HTTP提交的方法。
   * <p/>
   * @return 服务器对URL请求的响应。
   */
  public HttpResponseMeta go(String addr, HttpMethod method, byte... data) throws IOException
  {
    URL url;
    HttpRequestMeta request;

    url = new URL(addr);

    if (!url.getProtocol().equals("http"))
    {
      throw new IllegalArgumentException("不支持的协议类型：" + url.getProtocol() + "");
    }

    /*
     * 构造HTTP请求。
     */
    request = new HttpRequestMeta();
    request.setVersion(HttpVersion.http_1_1);
    request.setMethod(method);
    request.setData(data);
    
    return this.send(url.getHost(), url.getPort() == -1 ? url.getDefaultPort() : url.getPort(), request, url, this.referer);
  }

  /**
   * 执行服务器内跳转。
   * <p/>
   * @param addr
  * 相对或绝对URL地址。
   * @param method
* 指定HTTP提交的方法。
   * <p/>
   * @return 服务器对URL请求的响应。
   */
  public HttpResponseMeta click(String addr, HttpMethod method, byte... data) throws IOException
  {
    if (this.referer == null)
    {
      throw new IllegalArgumentException("由于没有访问记录，无法生成相对地址。");
    }

    URL newURL;
    
    if (addr.indexOf("://") == -1)
    {
      newURL = new URL(
      this.referer.getProtocol() + "://" + this.referer.getHost() + ":"
      + (this.referer.getPort() == -1 ? this.referer.getDefaultPort() : this.referer.getPort()) + "/"
      + (addr.startsWith("/") ? addr.substring(1)
         : ((this.referer.getPath() == null || this.referer.getPath().isEmpty()) ? addr
            : (this.referer.getPath().substring(0, this.referer.getPath().lastIndexOf('/')) + addr))));
    }
    else
    {
      newURL = new URL(addr);
    }

    return this.go(newURL.toString(), method, data);
  }

  /**
   * 内部HTTP通信方法。
   */
  private HttpResponseMeta send(String host, int port, HttpRequestMeta request, URL currentURL, URL beforeURL) throws IOException
  {
    HttpResponseMeta response;

    // 初始化网络连接。
    this.initializeNetwork(host, port);

    NetworkSessionInputStream in = new NetworkSessionInputStream(this.clientSocket);
    NetworkSessionOutputStream out = new NetworkSessionOutputStream(this.clientSocket);

    /*
     * 生成请求。
     */
    request.setRequestURI((currentURL.getPath().length() == 0 ? "/"
                           : (currentURL.getPath().startsWith("/") ? currentURL.getPath() : "/" + currentURL.getPath()))
                          + (currentURL.getQuery() == null ? "" : "?" + currentURL.getQuery()));
    request.getOthers().put("Content-Length", request.getData() == null ? "0" : String.valueOf(request.getData().length));
    request.getOthers().put("Accept", MIMEType.all.value);
    request.getOthers().put("Connection", ConnectionType.close.value);
    request.getOthers().put("Cache-Control", CacheControl.noCache.value);
    request.getOthers().put("User-Agent", this.userAgent);
    request.getOthers().put("Host", currentURL.getHost());
    if (beforeURL != null)
    {
      request.getOthers().put("Referer", beforeURL.toString());
    }
    if (!this.cookie.isEmpty())
    {
      request.getOthers().put("Cookie", this.cookie.get());
    }
//    log.debug(request.getRequest());
    this.lastRequest = request;
    out.write(request.getRequest());
    if (request.getData() != null)
    {
      out.write(request.getData());
    }

    /*
     * 解析响应。
     */
    response = HttpResponseMeta.parse(in);
    
    if (response == null)
    {
      throw new IllegalStateException("no response.");
    }
//    log.debug(response.getResponse());

    if (response.getOthers().get("Content-Length") != null)
    {
      response.setData(in.requiredRead(Integer.parseInt(response.getOthers().get("Content-Length"))));
    }
    else if (response.getOthers().get("Content-Length") == null || ConnectionType.get(response.getOthers().get("Connection")) == ConnectionType.close)
    {
      response.setData(IOUtils.read(in));
    }
    // 保存服务器COOKIE信息。
    if (response.getOthers().get("Set-Cookie") != null)
    {
      this.cookie.set(response.getOthers().get("Set-Cookie"));
    }

    if (response.getStatusCode() == 301 || response.getStatusCode() == 302)
    {
      HttpRequestMeta newRequest = new HttpRequestMeta();
      URL newURL;

      if (response.getOthers().get("Location").indexOf("://") != -1)
      {
        /*
         * 目标资源已经移动（绝对位置）。
         */
        newURL = new URL(response.getOthers().get("Location"));
      }
      else
      {
        /*
         * 目标资源已经移动（相对位置）。
         */
        newURL = new URL(
        currentURL.getProtocol()
        + "://"
        + currentURL.getHost()
        + ":"
        + (currentURL.getPort() == -1 ? currentURL.getDefaultPort() : currentURL.getPort())
        + ((currentURL.getPath() == null || currentURL.getPath().isEmpty()) ? "/" : currentURL.getPath())
        + (response.getOthers().get("Location").startsWith("/") ? response.getOthers().get("Location").substring(1) : response.getOthers().get(
           "Location")));
      }

      newRequest.setVersion(request.getVersion());
      newRequest.setMethod(request.getMethod());
      this.referer = newURL;

      return this.send(newURL.getHost(), newURL.getPort() == -1 ? newURL.getDefaultPort() : newURL.getPort(), newRequest, newURL, currentURL);
    }
    else
    {
      this.referer = currentURL;

      return response;
    }
  }
  
  public HttpRequestMeta getLastRequest()
  {
    return this.lastRequest;
  }

  public static void fetch(String url) throws IOException
  {
    HttpClient http = new HttpClient();
    HttpResponseMeta rsp;

    rsp = http.go(url, HttpMethod.get);

    if (rsp.getData() != null)
    {
      String html = new String(rsp.getData());
      System.out.println("视频名称：" + PatternUtils.fetchParameter("span id=\"subtitle\">%</span>", html));
      System.out.println("视频信息："
                         + PatternUtils.fetchParameter(
      "								<div class=\"item\">%<a onclick=\"Display\\.changeBlock\\('long', 'short'\\);getReference\\(\\);\">全部视频信息</a></div>", html));
      // System.out.println("视频页面地址：" +
      // PatternUtils.fetchParameter("id=\"link1\" value=\"%\" />", html));
      // System.out.println("视频动画地址：" +
      // PatternUtils.fetchParameter("id=\"link2\" value=\"%\" />", html));
    }
  }

  public static void main2(String[] args) throws IOException
  {
    HttpClient http = new HttpClient();
    HttpResponseMeta rsp;
    rsp = http.go("http://movie.youku.com/", HttpMethod.get);

    if (rsp.getData() != null)
    {
      String html = new String(rsp.getData());//(?<=<a title="abc" href=").*?(?=")
      System.out.println(html);
      //
//     System.out.println(PatternUtils.getSimpleFetchPattern("(?<=<a title=\".*\" href=\").*?(?=\")"));
//     System.out.println("视频列表：" +
//     PatternUtils.fetchParameter("(?<=p_link\"><a title=\".*\" href=\").*?(?=\"\\ charset)",
//     html));
    }

//    fetch("http://v.youku.com/v_show/id_XMTk3NjYwMjAw.html");
  }

}
